我們今天會完成 Gaussian Mixture Model 實做的部份。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
import cv2
from PIL import Image
class GaussianMixture:
"""Gaussian Mixture Model
initial centrol using k-meas solution.
"""
def __init__(self, k=3, max_iter=100):
self.k = k
self.max_iter = max_iter
self.loglikelihood = []
def init(self, X, parameters=None):
self.X = X
self.sample_size, self.n_features = X.shape # (76480, 3)
if parameters is not None:
centroid, labels = parameters
pi = [np.mean(labels == k) for k in range(self.k)] # (3, )
mu = centroid # (3, 3)
cov = [np.cov(X[np.where(labels==k)[0]].T) for k in range(self.k)] # (3, 3, 3)
return np.array(pi), np.array(mu), np.array(cov)
def EM(self, pi, mu, cov):
# initialization
self.pi, self.mu, self.cov = pi, mu, cov
for n_iter in range(self.max_iter):
# E step
self._resp, self.likelihood = self._e_step(self.pi, self.mu, self.cov)
# M step
self.pi, self.mu, self.cov = self._m_step(self._resp)
# recode log likelihood
self.recode_likelihood(self.likelihood)
return self.likelihood
def _e_step(self, pi, mu, cov):
# calculate likelihood
likelihood = np.array([multivariate_normal.pdf(self.X, mean=mu[k], cov=cov[k], allow_singular=True) for k in range(self.k)]) # (3, 76480)
resp = np.array(likelihood / np.sum(likelihood, axis=0)).T # (76480, 3)
return resp, likelihood
def _m_step(self, resp):
N_k = np.sum(resp, axis=0) # (3,)
pi = N_k / self.sample_size # (3,)
mu = np.dot(resp.T, self.X) / N_k[:, np.newaxis] # (3, 3)
cov = [ (1/N_k[k]) * np.dot((resp[:, k, np.newaxis]*(self.X - mu[k])).T, (self.X - mu[k])) for k in range(self.k)]
return pi, mu, cov
def recode_likelihood(self, likelihood):
# log likelihood
loglikelihood = np.sum(np.log(np.sum(likelihood, axis=0)))
self.loglikelihood.append(loglikelihood)
def predict(self, X):
likelihood = np.array([self.pi[k]*self.N(self.mu[k], self.cov[k]) for k in range(self.k)])
return np.argmax(likelihood, axis=0)
def plot_image(self, image, likelihood):
high, width, color_shape = image.shape
idx = np.argmax(likelihood, axis=0)
img = (self.mu[idx]*255).astype(int)
img = Image.fromarray(img.reshape(high, width, color_shape).astype('uint8'))
plt.axis('off')
plt.imshow(img)
plt.show()
__init__(self, k, max_iter)
初始化高斯混合模型,可以設定混合成分數 (k
) 和最大迭代次數 (max_iter
),並初始化記錄對數似然值的空列表。
init(self, X, parameters)
初始化模型參數。
它接受輸入數據 X
,以及可選的初始化參數 parameters
。如果提供了初始化參數,它將使用這些參數,否則將隨機初始化模型的參數(混合比例 pi
、均值 mu
和協方差矩陣 cov
)。
EM(self, pi, mu, cov)
實現期望最大化 (Expectation-Maximization, EM) 算法,用於訓練高斯混合模型。它迭代進行 E 步和 M 步,計算混合成分的參數(混合比例 pi
、均值 mu
和協方差矩陣 cov
),並記錄對數似然值。
_e_step
方法實現 EM 算法中的 E 步。它計算每個數據點屬於每個混合成分的概率,並計算整體似然值。_m_step
方法實現 EM 算法中的 M 步。它使用在 E 步中計算的概率,更新混合成分的參數。recode_likelihood(self, likelihood)
用於記錄每次迭代的對數似然值,這有助於監控模型訓練的進展。
predict(self, X)
用於根據訓練好的模型參數對新數據進行預測,它計算每個混合成分生成新數據的概率,然後選擇概率最高的混合成分作為預測結果。
plot_image(self, image, likelihood)
用於根據模型的似然值來繪製圖像。
它根據每個數據點在混合成分中的概率,選擇最有可能的混合成分,然後將其均值轉換為圖像像素值並顯示出來。
np.random.seed(42)
def euclidean_distance(x1, x2):
return np.sqrt(np.sum((x1 - x2)**2))
class KMeans():
def __init__(self, K=5, max_iters=100, plot_steps=False):
self.K = K
self.max_iters = max_iters
self.plot_steps = plot_steps
# list of sample indices for each cluster
self.clusters = [[] for _ in range(self.K)]
# the centers (mean feature vector) for each cluster
self.centroids = []
def predict(self, X):
self.X = X
self.n_samples, self.n_features = X.shape
# initialize
random_sample_idxs = np.random.choice(self.n_samples, self.K, replace=False)
self.centroids = [self.X[idx] for idx in random_sample_idxs]
# Optimize clusters
for _ in range(self.max_iters):
# Assign samples to closest centroids (create clusters)
self.clusters = self._create_clusters(self.centroids)
if self.plot_steps:
self.plot()
# Calculate new centroids from the clusters
centroids_old = self.centroids
self.centroids = self._get_centroids(self.clusters)
# check if clusters have changed
if self._is_converged(centroids_old, self.centroids):
break
if self.plot_steps:
self.plot()
# Classify samples as the index of their clusters
return self._get_cluster_labels(self.clusters)
def _get_cluster_labels(self, clusters):
# each sample will get the label of the cluster it was assigned to
labels = np.empty(self.n_samples)
for cluster_idx, cluster in enumerate(clusters):
labels[cluster] = cluster_idx
return labels
def _create_clusters(self, centroids):
# Assign the samples to the closest centroids to create clusters
clusters = [[] for _ in range(self.K)]
for idx, sample in enumerate(self.X):
centroid_idx = self._closest_centroid(sample, centroids)
clusters[centroid_idx].append(idx)
return clusters
def _closest_centroid(self, sample, centroids):
# distance of the current sample to each centroid
distances = [euclidean_distance(sample, point) for point in centroids]
closest_index = np.argmin(distances)
return closest_index
def _get_centroids(self, clusters):
# assign mean value of clusters to centroids
centroids = np.zeros((self.K, self.n_features))
for cluster_idx, cluster in enumerate(clusters):
cluster_mean = np.mean(self.X[cluster], axis=0)
centroids[cluster_idx] = cluster_mean
return centroids
def _is_converged(self, centroids_old, centroids):
# distances between each old and new centroids, fol all centroids
distances = [euclidean_distance(centroids_old[i], centroids[i]) for i in range(self.K)]
return sum(distances) == 0
def plot(self):
fig, ax = plt.subplots(figsize=(12, 8))
for i, index in enumerate(self.clusters):
point = self.X[index].T
ax.scatter(*point)
for point in self.centroids:
ax.scatter(*point, marker="x", color='black', linewidth=2)
plt.show()
def cent(self):
return self.centroids
__init__(self, K, max_iters, plot_steps)
初始化 K 值(要聚類的群數)、最大迭代次數、是否顯示步驟性的圖形等參數。它也初始化了用於存儲每個簇的樣本索引和每個簇的中心點。
predict(self, X)
用於執行 K-Means 聚類。它首先隨機初始化 K 個中心點,然後反覆執行以下步驟:
_get_cluster_labels(self, clusters)
將樣本分配給它們所屬的簇,並返回樣本的標籤。
_create_clusters(self, centroids)
根據最近的中心點將樣本分配到不同的簇中。
_closest_centroid(self, sample, centroids)
計算樣本到中心點的最短歐幾里得距離,以確定它屬於哪個簇。
_get_centroids(self, clusters)
計算每個簇的新中心點,即該簇中所有樣本的平均值。
_is_converged(self, centroids_old, centroids)
檢查中心點是否收斂,即新中心點和舊中心點之間的距離是否為零。
plot(self)
用於將簇和中心點的圖形可視化。
cent(self)
返回計算出的中心點。
image = cv2.imread("/kaggle/input/cute-cat/cat.jpg")
def gmm_preprocess(image):
# convert image to 2D array
pixel_values = image.reshape((-1, 3))
pixel_values = pixel_values/255
return pixel_values
image
讀取我們想呈現的圖片
並將image
轉成二維陣列
k_slot = [2, 3, 7, 20]
for k_idx in k_slot:
k = KMeans(K=k_idx, max_iters=100)
print("K = ", k_idx)
pixel_values = gmm_preprocess(image)
pixel_values = pixel_values[:, [2, 1, 0]]
y_pred = k.predict(pixel_values)
centers = np.uint8(k.cent())
model_GMM = GaussianMixture(k=k_idx, max_iter=100)
pi, mu, cov = model_GMM.init(pixel_values, parameters=(centers, y_pred))
likelihood = model_GMM.EM(pi, mu, cov)
# normalize likelihood
likelihood = likelihood / np.sum(likelihood, axis=0)
model_GMM.plot_image(image, likelihood)
使用 KMeans 和 Gaussian Mixture Model 來處理一張圖像。
過程中,建立一個K均值(KMeans)的模型,設定K值為k_idx
,最大迭代次數為100。
對圖像進行一些預處理,可能包括轉換顏色通道等操作。
使用 KMeans 對處理後的像素值進行聚類,得到每個像素的分類結果y_pred
。
計算 KMeans 的中心點centers
。
之後建立一個 GMM 模型(高斯混合模型),同樣設定K值為k_idx
,最大迭代次數為100。
進行 GMM 模型的初始化,使用先前得到的 KMeans的中心點和分類結果。
使用期望最大化(EM)算法對GMM模型進行擬合,得到每個像素屬於不同高斯分布的機率。
正規化機率,確保總和為1。
最後,使用GMM模型將圖像可視化,根據像素屬於不同高斯分布的機率,可能會產生一個有趣的圖像效果。
詳細 Notebook 可以參考 Kaggle
明天要進入真實實戰部份,會透過 Kaggle Dataset - Mall Customer Segmentation Data 來演練